feat(Init): Initial upload of array + interoperability package#1
feat(Init): Initial upload of array + interoperability package#1Simpag wants to merge 10 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Initial import of the decent_array package, providing an Array wrapper and a single-active-backend interoperability layer with NumPy/PyTorch/JAX/TensorFlow backends, plus CI, docs, benchmarks, and a comprehensive pytest suite.
Changes:
- Added backend manager + abstract backend contract, plus concrete backends (NumPy/PyTorch/JAX/TensorFlow) and module-level iop functions/RNG coordination.
- Added
Arraywrapper implementing operators, indexing, and common properties via the active backend. - Added tox/CI/Sphinx/ReadTheDocs scaffolding, benchmarks, and extensive tests.
Reviewed changes
Copilot reviewed 49 out of 52 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_iop_rng.py | Tests for RNG seeding, snapshot/restore, and distribution helpers. |
| tests/test_iop_functions.py | Tests for module-level interoperability functions (creation, math, linalg, indexing). |
| tests/test_decorators.py | Tests for the cost-method autodecorator argument unwrapping/rewrapping behavior. |
| tests/test_backend_manager.py | Tests backend activation, registration, listener behavior, and reset semantics. |
| tests/test_array.py | Tests Array operators/dunders, indexing, coercion, and properties across backends. |
| tests/conftest.py | Parametrized backend/device fixture with skip logic for unavailable combinations. |
| readthedocs.yaml | Read the Docs build configuration. |
| pyproject.toml | Project metadata, dependencies/extras, tox envs, ruff/mypy/pytest config, mypyc build hook. |
| LICENSE | Adds AGPL-3.0 license text. |
| docs/sphinx_theme.txt | Sphinx theme requirements list. |
| docs/source/user.rst | Initial user guide stub + installation snippet. |
| docs/source/index.rst | Sphinx landing page and toctree. |
| docs/source/developer.rst | Developer guide covering tox workflows, mypyc usage, and contribution process. |
| docs/source/conf.py | Sphinx configuration (theme, intersphinx, missing-reference fixups, sidebar config). |
| docs/source/background.rst | Background page stub. |
| docs/source/author.rst | Contributors page. |
| docs/source/api/decent_array.types.rst | API stub for decent_array.types. |
| docs/source/api/decent_array.rst | API toctree root. |
| docs/source/api/decent_array.interoperability.rst | API stub for decent_array.interoperability. |
| docs/source/api/decent_array.array.rst | API stub for decent_array (exports Array). |
| docs/source/_templates/package.rst.jinja | Custom sphinx-apidoc package template. |
| docs/source/_templates/module.rst.jinja | Custom sphinx-apidoc module template. |
| docs/source/_static/custom.css | Small theme CSS tweak for logo sizing. |
| docs/Makefile | Sphinx Makefile (unix). |
| docs/make.bat | Sphinx make script (windows). |
| decent_array/types.py | Shared type aliases + supported framework/device enums. |
| decent_array/interoperability/_tensorflow/tensorflow_backend.py | TensorFlow backend implementation + registration side-effect. |
| decent_array/interoperability/_tensorflow/init.py | TensorFlow backend package initializer/export. |
| decent_array/interoperability/_pytorch/pytorch_backend.py | PyTorch backend implementation + registration side-effect. |
| decent_array/interoperability/_pytorch/init.py | PyTorch backend package initializer/export. |
| decent_array/interoperability/_numpy/numpy_backend.py | NumPy backend implementation + registration side-effect. |
| decent_array/interoperability/_numpy/init.py | NumPy backend package initializer/export. |
| decent_array/interoperability/_jax/jax_backend.py | JAX backend implementation + registration side-effect. |
| decent_array/interoperability/_jax/init.py | JAX backend package initializer/export. |
| decent_array/interoperability/_iop/rng.py | Cross-backend RNG coordination (python random, numpy, backend) + distributions. |
| decent_array/interoperability/_iop/functions.py | Module-level iop function surface delegating to the active backend. |
| decent_array/interoperability/_iop/init.py | iop package marker/init (empty). |
| decent_array/interoperability/_decorators.py | Decorator to unwrap Array args for backend-native cost-method implementations. |
| decent_array/interoperability/_backend_manager.py | Backend registry/activation/reset + auto-import and listener mechanism. |
| decent_array/interoperability/_abstracts/backend.py | Abstract Backend API contract (creation/manipulation/linalg/math/RNG). |
| decent_array/interoperability/_abstracts/init.py | Abstracts package export (Backend). |
| decent_array/interoperability/init.py | Public interoperability API exports (functions + RNG helpers + decorator). |
| decent_array/_array.py | Array wrapper implementation (operators, indexing, properties) tied to active backend. |
| decent_array/init.py | Package exports (Array, interoperability, types). |
| benchmarks/profile_hotpath.py | cProfile-based hot-path profiler for wrapper vs function dispatch. |
| benchmarks/bench_iop.py | Microbenchmark for iop function-call overhead vs native frameworks. |
| benchmarks/bench_common.py | Shared benchmark helpers (backend discovery/activation + timing utilities). |
| benchmarks/bench_array.py | Microbenchmark for Array operator overhead vs native frameworks. |
| .gitignore | Python/build/dev artifact ignores. |
| .github/workflows/ci.yaml | CI pipeline running tox envs (mypy/pytest/ruff/sphinx/mypyc) on Py3.13 across OSes. |
| .github/CODEOWNERS | Sets default code owners for the repo. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| [project] | ||
| name = "decent-array" | ||
| version = "0.1.0" | ||
| authors = [{name = "Elias Ram"}, {name = "Simon Granström"}, {name = "Adriana Rodriguez"}, {name = "Nicola Bastianello"}] | ||
| maintainers = [{name = "Team Decent"}] | ||
| description = "A library of array operations and linear algebra primitives for interoperability across ML frameworks." | ||
| readme = "README.md" | ||
| requires-python = ">=3.13" | ||
| classifiers = [ | ||
| "Programming Language :: Python :: 3.13", | ||
| "Operating System :: OS Independent", | ||
| ] | ||
| license = "AGPL-3.0-only" | ||
| dependencies = [ | ||
| "numpy", | ||
| ] |
|
I will try to remember to upload benchmark results tomorrow evening |
nicola-bastianello
left a comment
There was a problem hiding this comment.
first batch of comments, mostly on docstrings and docs. a few of the comments on adding stuff to docs we can copy-paste to a new issue and address them later
| @@ -0,0 +1,71 @@ | |||
| """ | |||
There was a problem hiding this comment.
it would be nice to add instructions on how to run the benchmarks and interpret the results; maybe we add this when preparing the docs
| """ | ||
| Abstract :class:`Backend` contract. | ||
|
|
||
| All abstract methods live in this single class rather than across six mixin ABCs. The |
There was a problem hiding this comment.
this intro can be edited to remove mention of mixin, and the rest of the discussion should be included in/moved to the docs
| @@ -0,0 +1,301 @@ | |||
| """ | |||
| JAX backend for interoperability_2. | |||
There was a problem hiding this comment.
some mentions of interoperability_2 still present
| package can be auto-loaded on the first ``set_backend("tensorflow")`` call. | ||
|
|
||
| TF eager Tensors are immutable, so :meth:`set_item` round-trips through numpy and the | ||
| in-place math operations rebind the wrapper's underlying value. |
There was a problem hiding this comment.
a discussion on this should be included in the docs; let's open an issue about docs to keep track of what we should include
|
|
||
| Args: | ||
| backend: A :class:`~decent_array.types.SupportedFrameworks` value, its canonical string (e.g. | ||
| ``"numpy"``, ``"pytorch"``), or any alias declared by the backend at |
There was a problem hiding this comment.
mention of alias should be removed
| decent\_array | ||
| ============= | ||
|
|
||
| .. toctree:: |
There was a problem hiding this comment.
I would change the order: array, interoperability, types
| ============ | ||
|
|
||
| decent-array is developed by `Simon Granström <https://github.com/Simpag/>`_ and | ||
| `Adriana Rodriguez <https://github.com/adrianardv/>`_, under the supervision of |
There was a problem hiding this comment.
if Adriana has not directly contributed, we can remove her name for now. here I'd keep only people who have committed
|
|
||
|
|
||
|
|
||
| Compiled hot path (mypyc) |
There was a problem hiding this comment.
when running tox -e mypyc for the first time I got error: Microsoft Visual C++ 14.0 or greater is required. Get it with "Microsoft C++ Build Tools": https://visualstudio.microsoft.com/visual-cpp-build-tools/
I think here we should mention this possibility, and how to solve it for windows, I don't know about linux/max: 1. go to that website, 2. click download build tools, run the .exe, 3. select "C++ build tools" and install. this is going to take a while and install >6GB of stuff (not ideal, but it looks like there's no way around this storage footprint)
There was a problem hiding this comment.
Yes, you need a C compiler to make this work. Can add it to the docs
| @@ -0,0 +1,12 @@ | |||
| User Guide | |||
| ========== | |||
| This user guide shows you different examples of how to use decent-array. | |||
There was a problem hiding this comment.
sentence can be removed for now maybe? and we add to the docs todo list adding examples here, but I'd say it's lower priority
There was a problem hiding this comment.
All docs are pure boilerplate. I copied them from decent-bench
| @@ -0,0 +1,7 @@ | |||
| decent\_array.interoperability | |||
There was a problem hiding this comment.
we should probably find a way to divide the interop api in different pages, each collecting a category of functions (math, linalg, rng, etc). we can discuss the best division
nicola-bastianello
left a comment
There was a problem hiding this comment.
second batch of comments on repo set-up
| @@ -0,0 +1,11 @@ | |||
| **/__pycache__ | |||
There was a problem hiding this comment.
maybe we should add .pyd?
There was a problem hiding this comment.
on my system at least they are not ignored by vscode source control
There was a problem hiding this comment.
I did not get those on linux, I'll add more python metadata to the ignore list
| [project] | ||
| name = "decent-array" | ||
| version = "0.1.0" | ||
| authors = [{name = "Elias Ram"}, {name = "Simon Granström"}, {name = "Adriana Rodriguez"}, {name = "Nicola Bastianello"}] |
There was a problem hiding this comment.
to be changed to you and me only
| Issues = "https://github.com/team-decent/decent-array/issues" | ||
|
|
||
| [project.optional-dependencies] | ||
| dev = [ |
There was a problem hiding this comment.
I'm not sure about the division of optional dependencies here; maybe we should have separate items for torch, tensorflow and jax? or also put torch in dev-cpu and dev-gpu instead of in dev (to be "democratic")
also, "scipy-stubs", "types-networkx", "types-tabulate", might not be needed
There was a problem hiding this comment.
the dev, dev-cpu and dev-gpu are interconnected. A dev environment will have dev + dev-cpu or dev + dev-gpu. The split between dev-cpu and dev-gpu is only performed because they require different sub-packages to be installed properly. Torch can install on cpu only devices and gpu devices using the same method. We could move torch to dev-cpu but we'd have to force torch to only install cpu components.
| mypy-args = ["--ignore-missing-imports"] | ||
|
|
||
| [tool.tox] | ||
| envlist = ["dev", "mypy", "pytest", "ruff", "sphinx"] |
There was a problem hiding this comment.
during my own dev work, I've found that I like having time-consuming checks later; maybe we could change this order to: dev, ruff, mypy, pytest, sphinx? this way if ruff and mypy fail early, one can cancel execution of the checks, instead of waiting for pytest to finish before seeing if ruff is failing
of course one can argue about priorities, that pytest is more important.. let's discuss
There was a problem hiding this comment.
I know that one can run the checks individually, but sometimes I'm overly optimistic and run tox directly thinking all checks will be green
There was a problem hiding this comment.
The full tox command should generally only be performed right before commit, we might want to add a lint environment that calls ruff + mypy
nicola-bastianello
left a comment
There was a problem hiding this comment.
third batch of comments, this time on aligning to the array api
some of the signatures proposed by array api are a bit more complicated than what we currently have. so for now we could just make small changes (like introducing / where needed), and leave bigger changes on the type annotations for later
| return self.transpose | ||
|
|
||
| @property | ||
| def any(self) -> bool: |
There was a problem hiding this comment.
all and any are not properties in the array api, I would remove them. transpose should be exposed only as T
https://data-apis.org/array-api/latest/API_specification/array_object.html#attributes
There was a problem hiding this comment.
All and any are properties of numpy, torch and jax. I tend to use them a lot because its convenient. Only tensorflow does not have it because of its unique way to handle arrays.
All these frameworks also expose T + transpose. While I don’t care mind removing it, I tend to use .transpose for readability over .T
| """Return the sum of the array and another array or a scalar.""" | ||
| return Array(self.value + (other.value if type(other) is Array else other)) | ||
|
|
||
| def __radd__(self, other: float) -> Array: |
There was a problem hiding this comment.
as per array api: The results of applying reflected operators must match their non-reflected equivalents. https://data-apis.org/array-api/latest/API_specification/array_object.html#reflected-operators
this means that type of other should be expanded to include Array
There was a problem hiding this comment.
Its not needed because if we add two arrays the __add__ will be called over __radd__. The other argument will never be of type Array in __radd__ so I'm not sure what they are talking about, might be poor wording on their end
| """Return the sum of the array and a scalar.""" | ||
| return Array(other + self.value) | ||
|
|
||
| def __sub__(self, other: Array | float) -> Array: |
There was a problem hiding this comment.
array.__sub__(other: int | float | complex | array, /) -> array
There was a problem hiding this comment.
According to PEP 484, float encapsulates int so int | float is redundant (our linting also highlights this). We could discuss if we want to support complex numbers or not. I believe that it is safe so we could switch, then complex would include int and float so no need to explicitly type this.
| """Return the subtraction of the array from a scalar.""" | ||
| return Array(other - self.value) | ||
|
|
||
| def __mul__(self, other: Array | float) -> Array: |
There was a problem hiding this comment.
array.__mul__(other: int | float | complex | array, /) -> array
| """Return the product of the array and a scalar.""" | ||
| return Array(other * self.value) | ||
|
|
||
| def __truediv__(self, other: Array | float) -> Array: |
There was a problem hiding this comment.
array.__truediv__(other: int | float | complex | array, /) -> array
| """Element-wise greater-than.""" | ||
| return Array(self.value > (other.value if type(other) is Array else other)) | ||
|
|
||
| def __ge__(self, other: Array | float) -> Array: |
There was a problem hiding this comment.
array.__ge__(other: int | float | array, /) -> array
| # which fails TF's ``1 & bool_tensor`` rejection. Native operator semantics on | ||
| # the wrapped tensor enforce the actual dtype contract. | ||
|
|
||
| def __and__(self, other: Array | int) -> Array: |
There was a problem hiding this comment.
array.__and__(other: int | bool | array, /) -> array
|
|
||
| # Indexing ------------------------------------------------------------- | ||
|
|
||
| def __getitem__(self, key: ArrayKey) -> Array: |
There was a problem hiding this comment.
array.__getitem__(key: int | slice | ellipsis | None | Tuple[int | slice | ellipsis | array | None, ...] | array, /) -> array
the signature is a bit more complex than what we currently have, let's discuss if we want to expand it
There was a problem hiding this comment.
I've tried more complex key types but it very easily collapses or doesn’t compile
| """Return the item at ``key``.""" | ||
| return self._backend.get_item(self, key) | ||
|
|
||
| def __setitem__(self, key: ArrayKey, value: Array | float) -> None: |
There was a problem hiding this comment.
array.__setitem__(key: int | slice | ellipsis | Tuple[int | slice | ellipsis | array, ...] | array, value: int | float | complex | bool | array, /) -> None
|
|
||
| # Containers / iteration ---------------------------------------------- | ||
|
|
||
| def __len__(self) -> int: |
There was a problem hiding this comment.
__len__ is not in the array api, and I couldn't find an explanation. since all backeneds allow for this operation, let's keep it
nicola-bastianello
left a comment
There was a problem hiding this comment.
other comments, on private vs. public
| listener(_BACKEND_INSTANCE) | ||
|
|
||
|
|
||
| def register_backend_listener(listener: Callable[[Backend | None], None]) -> None: |
There was a problem hiding this comment.
register_backend_listener maybe should be private? I'm not sure I see a situation where a user would need this function
There was a problem hiding this comment.
These are not exposed to the public api but are used within the codebase which is why they are not explicitly "private". Generally private methods are only used within the same script, not cross modules
| _COORDINATOR.set_rng_state(state) | ||
|
|
||
|
|
||
| def derive_seed() -> int: |
There was a problem hiding this comment.
derive_seed should maybe be private? I think it would just be confusing for users.
but if we decide to keep it public, then the docs should explain in some detail how the seed is derived
There was a problem hiding this comment.
derive seed is needed for users to create their own rng system in a "deterministic random" way when a seed is set using iop.set_seed. For example in cost functions, datasets or schemes
| def __init__(self) -> None: | ||
| self._global_seed: int | None = None | ||
|
|
||
| def set_seed(self, seed: int, *, set_global_seed: bool = True) -> None: |
There was a problem hiding this comment.
set_global_seed is an option that might be confusing to users. we could consider spinning off a _set_local_seed function and remove the option from set_seed
There was a problem hiding this comment.
No methods in the _RngCoordinator are exposed. See set_seed futher down
| Run with:: | ||
|
|
||
| python benchmarks/bench_array.py | ||
| """ |
There was a problem hiding this comment.
it would be nice to have benchmarks that directly compare compiled and uncompiled versions; or at least have an option at the top to say which version should be used
There was a problem hiding this comment.
in any case all the benchmarks look good to me. I run them and the results of compilation are fantastic!
There was a problem hiding this comment.
The benchmarks print if they are compiled or not. I dont think there's an easy way to compare them in the same script. Could possibly do it using sub-processes but it would be rather intrusive because we'd need to delete compile files
nicola-bastianello
left a comment
There was a problem hiding this comment.
comments on the iop api
my priority for now is to ensure compatibility with the array api. this means changing the names and signatures of many functions, but at this stage we don't need to change the behavior (e.g. we can ignore arguments that are not currently supported). my idea is that aligning the api right now is easier than doing it later, and that changing behavior is easier to do at a later time
| # Array creation | ||
|
|
||
|
|
||
| def zeros(shape: tuple[int, ...]) -> Array: |
There was a problem hiding this comment.
array api suggests the following signature: zeros(shape: int | Tuple[int, ...], *, dtype: dtype | None = None, device: device | None = None) -> array https://data-apis.org/array-api/latest/API_specification/generated/array_api.zeros.html#array_api.zeros
they make device a kwarg; in the current setup, however, device choice is part of the backend. some options: 1) we avoid an explicit device kwarg, 2) we introduce it with default None and always ignore it (like numpy does), 3) we implement creating/moving arrays between different devices. I'm leaning towards 2), which would allow us to go for 3) in the future if we think it's interesting
in any case, I think we should type as int | Tuple[int, ...] like they do and expose dtype if possible
There was a problem hiding this comment.
same discussion applies to zeros_like, ones, ones_like, eye, etc..
There was a problem hiding this comment.
I dont see a reason to expose the device param if we want to keep it simple. It could easily cause device conflicts and users would have to think about device management
| return _BACKEND_INSTANCE.zeros(shape) | ||
|
|
||
|
|
||
| def zeros_like(array: Array) -> Array: |
There was a problem hiding this comment.
zeros_like(x: array, /, *, dtype: dtype | None = None, device: device | None = None) -> array
| return _BACKEND_INSTANCE.zeros_like(array) | ||
|
|
||
|
|
||
| def ones(shape: tuple[int, ...]) -> Array: |
There was a problem hiding this comment.
ones(shape: int | Tuple[int, ...], *, dtype: dtype | None = None, device: device | None = None) -> array
| return _BACKEND_INSTANCE.ones(shape) | ||
|
|
||
|
|
||
| def ones_like(array: Array) -> Array: |
There was a problem hiding this comment.
ones_like(x: array, /, *, dtype: dtype | None = None, device: device | None = None) → array
| return _BACKEND_INSTANCE.ones_like(array) | ||
|
|
||
|
|
||
| def eye(n: int) -> Array: |
There was a problem hiding this comment.
eye(n_rows: int, n_cols: int | None = None, /, *, k: int = 0, dtype: dtype | None = None, device: device | None = None) -> array
https://data-apis.org/array-api/latest/API_specification/generated/array_api.eye.html#array_api.eye
| return _BACKEND_INSTANCE.sign(array) | ||
|
|
||
|
|
||
| def maximum(array1: Array | float, array2: Array | float) -> Array: |
There was a problem hiding this comment.
maximum(x1: array | int | float, x2: array | int | float, /) -> array https://data-apis.org/array-api/latest/API_specification/generated/array_api.maximum.html#array_api.maximum
| return _BACKEND_INSTANCE.maximum(array1, array2) | ||
|
|
||
|
|
||
| def argmax(array: Array, axis: int | None = None, keepdims: bool = False) -> Array: |
There was a problem hiding this comment.
argmax(x: array, /, *, axis: int | None = None, keepdims: bool = False) -> array https://data-apis.org/array-api/latest/API_specification/generated/array_api.argmax.html#array_api.argmax
| return _BACKEND_INSTANCE.argmax(array, axis, keepdims) | ||
|
|
||
|
|
||
| def argmin(array: Array, axis: int | None = None, keepdims: bool = False) -> Array: |
There was a problem hiding this comment.
argmin(x: array, /, *, axis: int | None = None, keepdims: bool = False) -> array https://data-apis.org/array-api/latest/API_specification/generated/array_api.argmin.html#array_api.argmin
| return _BACKEND_INSTANCE.argmin(array, axis, keepdims) | ||
|
|
||
|
|
||
| def set_item(array: Array, key: ArrayKey, value: Array) -> None: |
There was a problem hiding this comment.
set_item and get_item could also be private, since they are called by the corresponding dunder methods
the signature should also align with the api as much as possible, e.g. array arg to x and the other args align to
array.__setitem__(key: int | slice | ellipsis | Tuple[int | slice | ellipsis | array, ...] | array, value: int | float | complex | bool | array, /) -> None https://data-apis.org/array-api/latest/API_specification/generated/array_api.array.__setitem__.html#array_api.array.__setitem__
array.__getitem__(key: int | slice | ellipsis | None | Tuple[int | slice | ellipsis | array | None, ...] | array, /) -> array https://data-apis.org/array-api/latest/API_specification/generated/array_api.array.__getitem__.html#array_api.array.__getitem__
| return (current_seed + random_data) % (2**32) | ||
|
|
||
|
|
||
| def normal(mean: float = 0.0, std: float = 1.0, shape: tuple[int, ...] = ()) -> Array: |
There was a problem hiding this comment.
random functions for array creation are not currently covered by the array api, so let's make our best guess at a good api, knowing that in the future we might need to break it if we want compatibility
normal, uniform, choice look good; but normal_like and uniform_like are not universally exposed. I would remove the two *_like functions, since it's not difficult to grab the shape from an array. if we decide to keep them, array arg should be renamed x
we could also include the dtype arg that e.g. torch, jax, tf expose
| return _BACKEND_INSTANCE.device_to_native(device) | ||
|
|
||
|
|
||
| def device_of(array: Array) -> SupportedDevices: |
There was a problem hiding this comment.
the array api suggests exposing device as a property of array objects. I think we should do the same, and make this function into a private utility. (as context: pytorch and tf also expose .device, in jax one needs .devices())
https://data-apis.org/array-api/latest/API_specification/generated/array_api.array.device.html
There was a problem hiding this comment.
the array api also suggests exposing:
default_device() -> devicehttps://data-apis.org/array-api/latest/API_specification/generated/array_api.info.default_device.html#array_api.info.default_devicedevices() -> Tuple[device, ...]https://data-apis.org/array-api/latest/API_specification/generated/array_api.info.devices.html#array_api.info.devices
I think we could already add default_device to return the backend device; devices we can discuss later
There was a problem hiding this comment.
another thing to discuss later is array.to_device(device: device, /, *, stream: int | Any | None = None) -> array https://data-apis.org/array-api/latest/API_specification/generated/array_api.array.to_device.html#array_api.array.to_device
this is part of the discussion of allowing device as an arg in array creation functions
| return _BACKEND_INSTANCE.eye_like(array) | ||
|
|
||
|
|
||
| def device_to_native(device: SupportedDevices) -> Any: # noqa: ANN401 |
There was a problem hiding this comment.
this should probably be private
| return _BACKEND_INSTANCE.copy(array) | ||
|
|
||
|
|
||
| def to_numpy(array: Array) -> NDArray[Any]: |
There was a problem hiding this comment.
both torch and tf use tensor.numpy() for conversion to numpy. we could change this function to private and call it from Array.numpy(). jax is a bit different but I think it's fair to use .numpy() as the api choice
related #3, which would require implementing __array__ and __array_wrap__, but that's a discussion for later
| return _BACKEND_INSTANCE.to_numpy(array) | ||
|
|
||
|
|
||
| def from_numpy(array: NDArray[Any]) -> Array: |
There was a problem hiding this comment.
all frameworks seem to do this differently, so let's keep the name from_numpy. only change: array arg to x as in the rest of the api
|
another comment: I like that with the new version there is no need for this would allow removing all checks from the iop functions. again, I'm not sure this is much of an improvement in terms of performance, but it would at least cut out a lot of repeated code |
I've already tried this and it doesnt compile. _NoBackend would not be of type Backend so it is not assignable to backend instance. So we'd need to implement every abstract method. The current solution was the best I could come up with without having some very complex code |
Initial upload of array + interoperability modules, including tests, docs, type-checking and compilation. Better docs and a README is needed.